package org.xlp.expression;

import java.util.Random;

/**
 * Description: 数学随机表达式生成器
 * <br/>date: 2024/11/30 0:00
 *
 * @version 1.0
 * @author: xlp
 */
public class RandomMathExpressionGenerator {
    private static final Random RANDOM = new Random();
    /**
     * 操作符
     */
    private static final String[] OPERATORS = {"+", "-", "*", "/"};
    /**
     * 1到100以内的表达式
     */
    private static final int MAX_OPERAND = 100;

    /**
     * 一次最多生成左括号的数量
     */
    private static final int MAX_LEFT_BRACKET_COUNT = 1;

    /**
     * 随机生成表达式最大深度
     */
    private static final int MAX_DEPTH = 10;

    /**
     * 表达式项数
     */
    private int depth = RANDOM.nextInt(MAX_DEPTH + 1);

    /**
     * 一次生成左括号的数量
     */
    private int leftBracketCount = MAX_LEFT_BRACKET_COUNT;

    /**
     * 操作符
     */
    private String[] operators = {"+", "-", "*", "/"};

    /**
     * 表达式复杂度
     */
    private ExpressionComplexity complexity = ExpressionComplexity.DECIMAL_ALL;

    /**
     * 最大值
     */
    private int max = MAX_OPERAND;

    /**
     * 最新值
     */
    private int min = 0;

    /**
     * 标记操作符与数据项之间是否添加空格，默认添加
     */
    boolean addSpace = true;

    private RandomMathExpressionGenerator() {}

    /**
     * 构建数学表达式对象
     * @return
     */
    public static RandomMathExpressionGenerator builder(){
        return new RandomMathExpressionGenerator();
    }

    /**
     * 设置表达式项数
     * @param depth
     * @return
     */
    public RandomMathExpressionGenerator depth(int depth){
        this.depth = depth;
        return this;
    }

    /**
     * 设置最大值
     * @param max 该值不能小于零
     * @return
     * @throws IllegalArgumentException 最多值小于零, 则抛出该异常
     */
    public RandomMathExpressionGenerator max(int max){
        if (max < 0){
            throw new IllegalArgumentException("最大值不能小于零");
        }
        this.max = max;
        return this;
    }

    /**
     * 设置取值范围
     * @param min 该值不能小于零
     * @param max 该值不能小于零
     * @return
     * @throws IllegalArgumentException 假如参数值小于0，则抛出该异常
     */
    public RandomMathExpressionGenerator range(int min, int max){
        if (min < 0 || max < 0){
            throw new IllegalArgumentException("最大值不能小于零");
        }
        this.max = Math.max(min, max);
        this.min = Math.min(min, max);
        return this;
    }

    /**
     * 设置表达式复杂度
     * @param complexity
     * @return
     */
    public RandomMathExpressionGenerator complexity(ExpressionComplexity complexity){
        if (complexity != null){
            this.complexity = complexity;

            switch (complexity){
                case INT_A_S:
                case DECIMAL_A_S:
                case INT_A_S_B:
                case DECIMAL_A_S_B:
                    operators = new String[]{"+", "-"};
                    break;
                case INT_M_D:
                case DECIMAL_M_D:
                case INT_M_D_B:
                case DECIMAL_M_D_B:
                    operators = new String[]{"*", "/"};
                    break;
                default:
                    operators = OPERATORS;
                    break;
            }
        }
        return this;
    }

    /**
     * 设置一次生成左括号的数量
     * @param leftBracketCount
     * @return
     */
    public RandomMathExpressionGenerator leftBracketCount(int leftBracketCount){
        this.leftBracketCount = Math.max(leftBracketCount, 1);
        return this;
    }

    /**
     * 设置操作符与数据项直接是否添加空格
     * @param addSpace
     * @return
     */
    public RandomMathExpressionGenerator addSpace(boolean addSpace){
        this.addSpace = addSpace;
        return this;
    }

    /**
     * 表达式生成
     * @return 返回生成的数学表达式，可能生成除数为0的表达式
     */
    public String generateExpression() {
        depth = Math.max(depth, 1);
        //假如深度小于2，直接返回一项
        if (depth < 2) return generateItem();

        int bracket = 0;
        //生成数据项
        boolean needBracket = needBracket();

        StringBuilder sb = new StringBuilder();
        for (int i = 1; i <= depth; i++){
            //假如是偶数，表示此时因生成操作符
            if (i != 1){
                //生成操作符
                sb.append(addSpace ? " " : "")
                        .append(operators[RANDOM.nextInt(operators.length)])
                        .append(addSpace ? " " : "");
            }

            int count = 0;
            if (needBracket && i != depth){
                //生成左括号
                count = bracket;
                bracket = generateLeftBracket(sb, bracket);
                //计算这次生成了几个左括号
                count = bracket - count;
            }

            sb.append(generateItem());

            if (needBracket){
                //生成右括号
                bracket = generateRightBracket(sb, bracket, count == 0);
            }
        }

        //用有括号匹配多余的左括号
        while (bracket > 0){
            sb.append(")");
            bracket--;
        }
        String expression = sb.toString();
        int moreCount = 0;
        int endIndex = expression.length() - 1;
        for (int i = 0; i <= endIndex; i++, endIndex--) {
            if (expression.charAt(endIndex) == ')'
                && expression.charAt(i) == '('){
                moreCount++;
            }
        }
        //去除首尾多余括号
        expression = expression.substring(moreCount, expression.length() - moreCount);
        return expression;
    }

    private boolean needBracket(){
        switch (complexity){
            case DECIMAL_A_S_B:
            case DECIMAL_M_D_B:
            case DECIMAL_ALL:
            case INT_A_S_B:
            case INT_ALL:
            case INT_M_D_B:
                return true;
            default:
                return false;
        }
    }

    /**
     * 数据项生成
     * @return
     */
    private String generateItem() {
        int value;
        if (min > 0 && RANDOM.nextBoolean()){
            value = RANDOM.nextInt(max - min + 1) + min;
        } else {
            value = RANDOM.nextInt(max);
            if (value < min){
                value = value + min;
            }
        }
        switch (complexity){
            case DECIMAL_M_D:
            case DECIMAL_ALL:
            case DECIMAL_A_S:
            case DECIMAL_A_S_B:
            case DECIMAL_A_S_M_D:
            case DECIMAL_M_D_B:
                int decimal = RANDOM.nextInt(MAX_OPERAND);
                if (value != 0 && decimal != 0){
                    return value + "." + decimal;
                }
        }
        return String.valueOf(value);
    }

    /**
     * 生成有括号
     * @param sb
     * @param bracket
     * @param generateMoreRightBracket 标记是否可连续生成多个有括号
     * @return
     */
    private int generateRightBracket(StringBuilder sb, int bracket, boolean generateMoreRightBracket) {
        boolean flag = isGeneratedBracket();
        while (generateMoreRightBracket && flag && bracket > 0) {
            sb.append(")");
            bracket--;
            flag = isGeneratedBracket();
        }
        return bracket;
    }

    /**
     * 生成左括号
     * @param sb
     * @param bracket
     * @return
     */
    private int generateLeftBracket(StringBuilder sb, int bracket) {
        boolean flag = isGeneratedBracket();
        int i = leftBracketCount;
        while (flag && i > 0) {
            sb.append("(");
            bracket++;
            i--;
            flag = isGeneratedBracket();
        }
        return bracket;
    }

    /**
     * 是否随机生成括号
     * @return 返回true：生成，false：不生成
     */
    protected boolean isGeneratedBracket(){
        return RANDOM.nextInt(MAX_OPERAND + 1) < 20;
    }
}
